package org.kxml2.io;

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.io.Reader;
import java.util.Hashtable;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;

public class KXmlParser
  implements XmlPullParser
{
  private Object location;
  private static final String UNEXPECTED_EOF = "Unexpected EOF";
  private static final String ILLEGAL_TYPE = "Wrong event type";
  private static final int LEGACY = 999;
  private static final int XML_DECL = 998;
  private String version;
  private Boolean standalone;
  private boolean processNsp;
  private boolean relaxed;
  private Hashtable entityMap;
  private int depth;
  private String[] elementStack = new String[16];
  private String[] nspStack = new String[8];
  private int[] nspCounts = new int[4];
  private Reader reader;
  private String encoding;
  private char[] srcBuf;
  private int srcPos;
  private int srcCount;
  private int line;
  private int column;
  private char[] txtBuf = new char[''];
  private int txtPos;
  private int type;
  private boolean isWhitespace;
  private String namespace;
  private String prefix;
  private String name;
  private boolean degenerated;
  private int attributeCount;
  private String[] attributes = new String[16];
  private String error;
  private int[] peek = new int[2];
  private int peekCount;
  private boolean wasCR;
  private boolean unresolved;
  private boolean token;

  public KXmlParser()
  {
    this.srcBuf = new char[Runtime.getRuntime().freeMemory() >= 1048576L ? ' ' : ''];
  }

  private final boolean isProp(String n1, boolean prop, String n2)
  {
    if (!n1.startsWith("http://xmlpull.org/v1/doc/"))
      return false;
    if (prop) {
      return n1.substring(42).equals(n2);
    }
    return n1.substring(40).equals(n2);
  }

  private final boolean adjustNsp() throws XmlPullParserException
  {
    boolean any = false;

    for (int i = 0; i < this.attributeCount << 2; i += 4)
    {
      String attrName = this.attributes[(i + 2)];
      int cut = attrName.indexOf(58);
      String prefix;
      if (cut != -1) {
        prefix = attrName.substring(0, cut);
        attrName = attrName.substring(cut + 1);
      } else {
        if (!attrName.equals("xmlns")) continue;
        prefix = attrName;
        attrName = null;
      }

      if (!prefix.equals("xmlns")) {
        any = true;
      }
      else
      {
        int tmp95_92 = this.depth;
        int[] tmp95_88 = this.nspCounts;
        int tmp97_96 = tmp95_88[tmp95_92]; tmp95_88[tmp95_92] = (tmp97_96 + 1); int j = tmp97_96 << 1;

        this.nspStack = ensureCapacity(this.nspStack, j + 2);
        this.nspStack[j] = attrName;
        this.nspStack[(j + 1)] = this.attributes[(i + 3)];

        if ((attrName != null) && (this.attributes[(i + 3)].equals(""))) {
          error("illegal empty namespace");
        }

        System.arraycopy(this.attributes, i + 4, this.attributes, i, (--this.attributeCount << 2) - i);

        i -= 4;
      }
    }

    if (any) {
      for (int i = (this.attributeCount << 2) - 4; i >= 0; i -= 4)
      {
        String attrName = this.attributes[(i + 2)];
        int cut = attrName.indexOf(58);

        if ((cut == 0) && (!this.relaxed)) {
          throw new RuntimeException("illegal attribute name: " + attrName + " at " + this);
        }

        if (cut != -1) {
          String attrPrefix = attrName.substring(0, cut);

          attrName = attrName.substring(cut + 1);

          String attrNs = getNamespace(attrPrefix);

          if ((attrNs == null) && (!this.relaxed)) {
            throw new RuntimeException("Undefined Prefix: " + attrPrefix + " in " + this);
          }

          this.attributes[i] = attrNs;
          this.attributes[(i + 1)] = attrPrefix;
          this.attributes[(i + 2)] = attrName;
        }

      }

    }

    int cut = this.name.indexOf(58);

    if (cut == 0) {
      error("illegal tag name: " + this.name);
    }
    if (cut != -1) {
      this.prefix = this.name.substring(0, cut);
      this.name = this.name.substring(cut + 1);
    }

    this.namespace = getNamespace(this.prefix);

    if (this.namespace == null) {
      if (this.prefix != null)
        error("undefined prefix: " + this.prefix);
      this.namespace = "";
    }

    return any;
  }

  private final String[] ensureCapacity(String[] arr, int required) {
    if (arr.length >= required)
      return arr;
    String[] bigger = new String[required + 16];
    System.arraycopy(arr, 0, bigger, 0, arr.length);
    return bigger;
  }

  private final void error(String desc) throws XmlPullParserException {
    if (this.relaxed) {
      if (this.error == null)
        this.error = ("ERR: " + desc);
    }
    else
      exception(desc);
  }

  private final void exception(String desc) throws XmlPullParserException {
    throw new XmlPullParserException(desc.substring(0, 100) + "\n", this, null);
  }

  private final void nextImpl()
    throws IOException, XmlPullParserException
  {
    if (this.reader == null) {
      exception("No Input specified");
    }
    if (this.type == 3)
      this.depth -= 1;
    do
    {
      this.attributeCount = -1;

      if (this.degenerated) {
        this.degenerated = false;
        this.type = 3;
        return;
      }

      if (this.error != null) {
        for (int i = 0; i < this.error.length(); i++) {
          push(this.error.charAt(i));
        }
        this.error = null;
        this.type = 9;
        return;
      }

      this.prefix = null;
      this.name = null;
      this.namespace = null;

      this.type = peekType();

      switch (this.type)
      {
      case 6:
        pushEntity();
        return;
      case 2:
        parseStartTag(false);
        return;
      case 3:
        parseEndTag();
        return;
      case 1:
        return;
      case 4:
        pushText(60, !this.token);
        if ((this.depth == 0) && 
          (this.isWhitespace)) {
          this.type = 7;
        }

        return;
      case 5:
      }
      this.type = parseLegacy(this.token);
    }while (this.type == 998);
  }

  private final int parseLegacy(boolean push)
    throws IOException, XmlPullParserException
  {
    String req = "";

    int prev = 0;

    read();
    int c = read();
    int result;
    if (c == 63) {
      if (((peek(0) == 120) || (peek(0) == 88)) && ((peek(1) == 109) || (peek(1) == 77)))
      {
        if (push) {
          push(peek(0));
          push(peek(1));
        }
        read();
        read();

        if (((peek(0) == 108) || (peek(0) == 76)) && (peek(1) <= 32))
        {
          if ((this.line != 1) || (this.column > 4)) {
            error("PI must not start with xml");
          }
          parseStartTag(true);

          if ((this.attributeCount < 1) || (!"version".equals(this.attributes[2]))) {
            error("version expected");
          }
          this.version = this.attributes[3];

          int pos = 1;

          if ((pos < this.attributeCount) && ("encoding".equals(this.attributes[6])))
          {
            this.encoding = this.attributes[7];
            pos++;
          }

          if ((pos < this.attributeCount) && ("standalone".equals(this.attributes[(4 * pos + 2)])))
          {
            String st = this.attributes[(3 + 4 * pos)];
            if ("yes".equals(st))
              this.standalone = new Boolean(true);
            else if ("no".equals(st))
              this.standalone = new Boolean(false);
            else
              error("illegal standalone value: " + st);
            pos++;
          }

          if (pos != this.attributeCount) {
            error("illegal xmldecl");
          }
          this.isWhitespace = true;
          this.txtPos = 0;

          return 998;
        }

      }

      int term = 63;
      result = 8;
    }
    else
    {
      int term;
      if (c == 33)
      {
        if (peek(0) == 45) {
          result = 9;
          req = "--";
          term = 45;
        }
        else if (peek(0) == 91) {
          result = 5;
          req = "[CDATA[";
          term = 93;
          push = true;
        }
        else {
          result = 10;
          req = "DOCTYPE";
          term = -1;
        }
      }
      else {
        error("illegal: <" + c);
        return 9;
      }
    }
    int term = 0;
    for (int i = 0; i < req.length(); i++) {
      read(req.charAt(i));
    }
    if (result == 10) {
      parseDoctype(push);
    } else {
      while (true) {
        c = read();
        if (c == -1) {
          error("Unexpected EOF");
          return 9;
        }

        if (push) {
          push(c);
        }
        if (((term == 63) || (c == term)) && (peek(0) == term) && (peek(1) == 62))
        {
          break;
        }

        prev = c;
      }

      if ((term == 45) && (prev == 45) && (!this.relaxed)) {
        error("illegal comment delimiter: --->");
      }
      read();
      read();

      if ((push) && (term != 63)) {
        this.txtPos -= 1;
      }
    }
    return result;
  }

  private final void parseDoctype(boolean push)
    throws IOException, XmlPullParserException
  {
    int nesting = 1;
    boolean quoted = false;
    while (true)
    {
      int i = read();
      switch (i)
      {
      case -1:
        error("Unexpected EOF");
        return;
      case 39:
        quoted = !quoted;
        break;
      case 60:
        if (quoted) break;
        nesting++; break;
      case 62:
        if (quoted) break;
        nesting--; if (nesting != 0) break;
        return;
      }

      if (push)
        push(i);
    }
  }

  private final void parseEndTag()
    throws IOException, XmlPullParserException
  {
    read();
    read();
    this.name = readName();
    skip();
    read('>');

    int sp = this.depth - 1 << 2;

    if (this.depth == 0) {
      error("element stack empty");
      this.type = 9;
      return;
    }

    if (!this.relaxed) {
      if (!this.name.equals(this.elementStack[(sp + 3)])) {
        error("expected: /" + this.elementStack[(sp + 3)] + " read: " + this.name);
      }

      this.namespace = this.elementStack[sp];
      this.prefix = this.elementStack[(sp + 1)];
      this.name = this.elementStack[(sp + 2)];
    }
  }

  private final int peekType() throws IOException {
    switch (peek(0)) {
    case -1:
      return 1;
    case 38:
      return 6;
    case 60:
      switch (peek(1)) {
      case 47:
        return 3;
      case 33:
      case 63:
        return 999;
      }
      return 2;
    }

    return 4;
  }

  private final String get(int pos)
  {
    return new String(this.txtBuf, pos, this.txtPos - pos);
  }

  private final void push(int c)
  {
    this.isWhitespace &= c <= 32;

    if (this.txtPos == this.txtBuf.length) {
      char[] bigger = new char[this.txtPos * 4 / 3 + 4];
      System.arraycopy(this.txtBuf, 0, bigger, 0, this.txtPos);
      this.txtBuf = bigger;
    }

    this.txtBuf[(this.txtPos++)] = (char)c;
  }

  private final void parseStartTag(boolean xmldecl)
    throws IOException, XmlPullParserException
  {
    if (!xmldecl)
      read();
    this.name = readName();
    this.attributeCount = 0;
    while (true)
    {
      skip();

      int c = peek(0);

      if (xmldecl) {
        if (c == 63) {
          read();
          read('>');
          return;
        }
      }
      else {
        if (c == 47) {
          this.degenerated = true;
          read();
          skip();
          read('>');
          break;
        }

        if ((c == 62) && (!xmldecl)) {
          read();
          break;
        }
      }

      if (c == -1) {
        error("Unexpected EOF");

        return;
      }

      String attrName = readName();

      if (attrName.length() == 0) {
        error("attr name expected");

        break;
      }

      int i = this.attributeCount++ << 2;

      this.attributes = ensureCapacity(this.attributes, i + 4);

      this.attributes[(i++)] = "";
      this.attributes[(i++)] = null;
      this.attributes[(i++)] = attrName;

      skip();

      if (peek(0) != 61) {
        if (!this.relaxed) {
          error("Attr.value missing f. " + attrName);
        }
        this.attributes[i] = attrName;
      }
      else {
        read('=');
        skip();
        int delimiter = peek(0);

        if ((delimiter != 39) && (delimiter != 34)) {
          if (!this.relaxed) {
            error("attr value delimiter missing!");
          }
          delimiter = 32;
        }
        else {
          read();
        }
        int p = this.txtPos;
        pushText(delimiter, true);

        this.attributes[i] = get(p);
        this.txtPos = p;

        if (delimiter != 32) {
          read();
        }
      }
    }
    int sp = this.depth++ << 2;

    this.elementStack = ensureCapacity(this.elementStack, sp + 4);
    this.elementStack[(sp + 3)] = this.name;

    if (this.depth >= this.nspCounts.length) {
      int[] bigger = new int[this.depth + 4];
      System.arraycopy(this.nspCounts, 0, bigger, 0, this.nspCounts.length);
      this.nspCounts = bigger;
    }

    this.nspCounts[this.depth] = this.nspCounts[(this.depth - 1)];

    if (this.processNsp)
      adjustNsp();
    else {
      this.namespace = "";
    }
    this.elementStack[sp] = this.namespace;
    this.elementStack[(sp + 1)] = this.prefix;
    this.elementStack[(sp + 2)] = this.name;
  }

  private final void pushEntity()
    throws IOException, XmlPullParserException
  {
    push(read());

    int pos = this.txtPos;
    while (true)
    {
      int c = peek(0);
      if (c == 59) {
        read();
        break;
      }
      if ((c < 128) && ((c < 48) || (c > 57)) && ((c < 97) || (c > 122)) && ((c < 65) || (c > 90)) && (c != 95) && (c != 45) && (c != 35))
      {
        if (!this.relaxed) {
          error("unterminated entity ref");
        }

        System.out.println("broken entitiy: " + get(pos - 1));

        return;
      }

      push(read());
    }

    String code = get(pos);
    this.txtPos = (pos - 1);
    if ((this.token) && (this.type == 6)) {
      this.name = code;
    }

    if (code.charAt(0) == '#') {
      int c = code.charAt(1) == 'x' ? Integer.parseInt(code.substring(2), 16) : Integer.parseInt(code.substring(1));

      push(c);
      return;
    }

    String result = (String)this.entityMap.get(code);

    this.unresolved = (result == null);

    if (this.unresolved) {
      if (!this.token)
        error("unresolved: &" + code + ";");
    }
    else
      for (int i = 0; i < result.length(); i++)
        push(result.charAt(i));
  }

  private final void pushText(int delimiter, boolean resolveEntities)
    throws IOException, XmlPullParserException
  {
    int next = peek(0);
    int cbrCount = 0;

    while ((next != -1) && (next != delimiter))
    {
      if ((delimiter == 32) && (
        (next <= 32) || (next == 62))) {
        break;
      }
      if (next == 38) {
        if (!resolveEntities) {
          break;
        }
        pushEntity();
      }
      else if ((next == 10) && (this.type == 2)) {
        read();
        push(32);
      }
      else {
        push(read());
      }
      if ((next == 62) && (cbrCount >= 2) && (delimiter != 93)) {
        error("Illegal: ]]>");
      }
      if (next == 93)
        cbrCount++;
      else {
        cbrCount = 0;
      }
      next = peek(0);
    }
  }

  private final void read(char c) throws IOException, XmlPullParserException
  {
    int a = read();
    if (a != c)
      error("expected: '" + c + "' actual: '" + (char)a + "'");
  }

  private final int read()
    throws IOException
  {
    int result;
    if (this.peekCount == 0) {
      result = peek(0);
    } else {
      result = this.peek[0];
      this.peek[0] = this.peek[1];
    }

    this.peekCount -= 1;

    this.column += 1;

    if (result == 10)
    {
      this.line += 1;
      this.column = 1;
    }

    return result;
  }

  private final int peek(int pos)
    throws IOException
  {
    while (pos >= this.peekCount)
    {
      int nw;
      if (this.srcBuf.length <= 1) {
        nw = this.reader.read();
      }
      else
      {
        if (this.srcPos < this.srcCount) {
          nw = this.srcBuf[(this.srcPos++)];
        } else {
          this.srcCount = this.reader.read(this.srcBuf, 0, this.srcBuf.length);
          if (this.srcCount <= 0)
            nw = -1;
          else {
            nw = this.srcBuf[0];
          }
          this.srcPos = 1;
        }
      }
      if (nw == 13) {
        this.wasCR = true;
        this.peek[(this.peekCount++)] = 10;
      }
      else {
        if (nw == 10) {
          if (!this.wasCR)
            this.peek[(this.peekCount++)] = 10;
        }
        else {
          this.peek[(this.peekCount++)] = nw;
        }
        this.wasCR = false;
      }
    }

    return this.peek[pos];
  }

  private final String readName()
    throws IOException, XmlPullParserException
  {
    int pos = this.txtPos;
    int c = peek(0);
    if (((c < 97) || (c > 122)) && ((c < 65) || (c > 90)) && (c != 95) && (c != 58) && (c < 192) && (!this.relaxed))
    {
      error("name expected");
    }
    do {
      push(read());
      c = peek(0);
    }

    while (((c >= 97) && (c <= 122)) || ((c >= 65) && (c <= 90)) || ((c >= 48) && (c <= 57)) || (c == 95) || (c == 45) || (c == 58) || (c == 46) || (c >= 183));

    String result = get(pos);
    this.txtPos = pos;
    return result;
  }

  private final void skip() throws IOException
  {
    while (true) {
      int c = peek(0);
      if ((c > 32) || (c == -1))
        break;
      read();
    }
  }

  public void setInput(Reader reader)
    throws XmlPullParserException
  {
    this.reader = reader;

    this.line = 1;
    this.column = 0;
    this.type = 0;
    this.name = null;
    this.namespace = null;
    this.degenerated = false;
    this.attributeCount = -1;
    this.encoding = null;
    this.version = null;
    this.standalone = null;

    if (reader == null) {
      return;
    }
    this.srcPos = 0;
    this.srcCount = 0;
    this.peekCount = 0;
    this.depth = 0;

    this.entityMap = new Hashtable();
    this.entityMap.put("amp", "&");
    this.entityMap.put("apos", "'");
    this.entityMap.put("gt", ">");
    this.entityMap.put("lt", "<");
    this.entityMap.put("quot", "\"");
  }

  public void setInput(InputStream is, String _enc)
    throws XmlPullParserException
  {
    this.srcPos = 0;
    this.srcCount = 0;
    String enc = _enc;

    if (is == null) {
      throw new IllegalArgumentException();
    }
    try
    {
      if (enc == null)
      {
        int chk = 0;

        while (this.srcCount < 4) {
          int i = is.read();
          if (i == -1)
            break;
          chk = chk << 8 | i;
          this.srcBuf[(this.srcCount++)] = (char)i;
        }

        if (this.srcCount == 4) {
          switch (chk) {
          case 65279:
            enc = "UTF-32BE";
            this.srcCount = 0;
            break;
          case -131072:
            enc = "UTF-32LE";
            this.srcCount = 0;
            break;
          case 60:
            enc = "UTF-32BE";
            this.srcBuf[0] = '<';
            this.srcCount = 1;
            break;
          case 1006632960:
            enc = "UTF-32LE";
            this.srcBuf[0] = '<';
            this.srcCount = 1;
            break;
          case 3932223:
            enc = "UTF-16BE";
            this.srcBuf[0] = '<';
            this.srcBuf[1] = '?';
            this.srcCount = 2;
            break;
          case 1006649088:
            enc = "UTF-16LE";
            this.srcBuf[0] = '<';
            this.srcBuf[1] = '?';
            this.srcCount = 2;
            break;
          case 1010792557:
            while (true)
            {
              int i = is.read();
              if (i == -1)
                break;
              this.srcBuf[(this.srcCount++)] = (char)i;
              if (i == 62) {
                String s = new String(this.srcBuf, 0, this.srcCount);
                int i0 = s.indexOf("encoding");
                if (i0 == -1)
                  break;
                while ((s.charAt(i0) != '"') && (s.charAt(i0) != '\''))
                  i0++;
                char deli = s.charAt(i0++);
                int i1 = s.indexOf(deli, i0);
                enc = s.substring(i0, i1);
                break;
              }

            }

          default:
            if ((chk & 0xFFFF0000) == -16842752) {
              enc = "UTF-16BE";
              this.srcBuf[0] = (char)(this.srcBuf[2] << '\b' | this.srcBuf[3]);

              this.srcCount = 1;
            }
            else if ((chk & 0xFFFF0000) == -131072) {
              enc = "UTF-16LE";
              this.srcBuf[0] = (char)(this.srcBuf[3] << '\b' | this.srcBuf[2]);

              this.srcCount = 1;
            } else {
              if ((chk & 0xFFFFFF00) != -272908544) break;
              enc = "UTF-8";
              this.srcBuf[0] = this.srcBuf[3];
              this.srcCount = 1;
            }
          }
        }
      }

      if (enc == null) {
        enc = "UTF-8";
      }
      int sc = this.srcCount;
      setInput(new InputStreamReader(is, enc));
      this.encoding = _enc;
      this.srcCount = sc;
    }
    catch (Exception e) {
      throw new XmlPullParserException("Invalid stream or encoding: " + e.toString(), this, e);
    }
  }

  public boolean getFeature(String feature)
  {
    if ("http://xmlpull.org/v1/doc/features.html#process-namespaces".equals(feature))
      return this.processNsp;
    if (isProp(feature, false, "relaxed")) {
      return this.relaxed;
    }
    return false;
  }

  public String getInputEncoding() {
    return this.encoding;
  }

  public void defineEntityReplacementText(String entity, String value) throws XmlPullParserException
  {
    if (this.entityMap == null)
      throw new RuntimeException("entity replacement text must be defined after setInput!");
    this.entityMap.put(entity, value);
  }

  public Object getProperty(String property) {
    if (isProp(property, true, "xmldecl-version"))
      return this.version;
    if (isProp(property, true, "xmldecl-standalone"))
      return this.standalone;
    if (isProp(property, true, "location"))
      return this.location != null ? this.location : this.reader.toString();
    return null;
  }

  public int getNamespaceCount(int depth) {
    if (depth > this.depth)
      throw new IndexOutOfBoundsException();
    return this.nspCounts[depth];
  }

  public String getNamespacePrefix(int pos) {
    return this.nspStack[(pos << 1)];
  }

  public String getNamespaceUri(int pos) {
    return this.nspStack[((pos << 1) + 1)];
  }

  public String getNamespace(String prefix)
  {
    if ("xml".equals(prefix))
      return "http://www.w3.org/XML/1998/namespace";
    if ("xmlns".equals(prefix)) {
      return "http://www.w3.org/2000/xmlns/";
    }
    for (int i = (getNamespaceCount(this.depth) << 1) - 2; i >= 0; i -= 2) {
      if (prefix == null) {
        if (this.nspStack[i] == null)
          return this.nspStack[(i + 1)];
      }
      else if (prefix.equals(this.nspStack[i]))
        return this.nspStack[(i + 1)];
    }
    return null;
  }

  public int getDepth() {
    return this.depth;
  }

  public String getPositionDescription()
  {
    StringBuffer buf = new StringBuffer(this.type < TYPES.length ? TYPES[this.type] : "unknown");

    buf.append(' ');

    if ((this.type == 2) || (this.type == 3)) {
      if (this.degenerated)
        buf.append("(empty) ");
      buf.append('<');
      if (this.type == 3) {
        buf.append('/');
      }
      if (this.prefix != null)
        buf.append("{" + this.namespace + "}" + this.prefix + ":");
      buf.append(this.name);

      int cnt = this.attributeCount << 2;
      for (int i = 0; i < cnt; i += 4) {
        buf.append(' ');
        if (this.attributes[(i + 1)] != null) {
          buf.append("{" + this.attributes[i] + "}" + this.attributes[(i + 1)] + ":");
        }
        buf.append(this.attributes[(i + 2)] + "='" + this.attributes[(i + 3)] + "'");
      }

      buf.append('>');
    }
    else if (this.type != 7) {
      if (this.type != 4) {
        buf.append(getText());
      } else if (this.isWhitespace) {
        buf.append("(whitespace)");
      } else {
        String text = getText();
        if (text.length() > 16)
          text = text.substring(0, 16) + "...";
        buf.append(text);
      }
    }
    buf.append("@" + this.line + ":" + this.column);
    if (this.location != null) {
      buf.append(" in ");
      buf.append(this.location);
    }
    else if (this.reader != null) {
      buf.append(" in ");
      buf.append(this.reader.toString());
    }
    return buf.toString();
  }

  public int getLineNumber() {
    return this.line;
  }

  public int getColumnNumber() {
    return this.column;
  }

  public boolean isWhitespace() throws XmlPullParserException {
    if ((this.type != 4) && (this.type != 7) && (this.type != 5))
      exception("Wrong event type");
    return this.isWhitespace;
  }

  public String getText() {
    return (this.type < 4) || ((this.type == 6) && (this.unresolved)) ? null : get(0);
  }

  public char[] getTextCharacters(int[] poslen)
  {
    if (this.type >= 4) {
      if (this.type == 6) {
        poslen[0] = 0;
        poslen[1] = this.name.length();
        return this.name.toCharArray();
      }
      poslen[0] = 0;
      poslen[1] = this.txtPos;
      return this.txtBuf;
    }

    poslen[0] = -1;
    poslen[1] = -1;
    return null;
  }

  public String getNamespace() {
    return this.namespace;
  }

  public String getName() {
    return this.name;
  }

  public String getPrefix() {
    return this.prefix;
  }

  public boolean isEmptyElementTag() throws XmlPullParserException {
    if (this.type != 2)
      exception("Wrong event type");
    return this.degenerated;
  }

  public int getAttributeCount() {
    return this.attributeCount;
  }

  public String getAttributeType(int index) {
    return "CDATA";
  }

  public boolean isAttributeDefault(int index) {
    return false;
  }

  public String getAttributeNamespace(int index) {
    if (index >= this.attributeCount)
      throw new IndexOutOfBoundsException();
    return this.attributes[(index << 2)];
  }

  public String getAttributeName(int index) {
    if (index >= this.attributeCount)
      throw new IndexOutOfBoundsException();
    return this.attributes[((index << 2) + 2)];
  }

  public String getAttributePrefix(int index) {
    if (index >= this.attributeCount)
      throw new IndexOutOfBoundsException();
    return this.attributes[((index << 2) + 1)];
  }

  public String getAttributeValue(int index) {
    if (index >= this.attributeCount)
      throw new IndexOutOfBoundsException();
    return this.attributes[((index << 2) + 3)];
  }

  public String getAttributeValue(String namespace, String name)
  {
    for (int i = (this.attributeCount << 2) - 4; i >= 0; i -= 4) {
      if ((this.attributes[(i + 2)].equals(name)) && ((namespace == null) || (this.attributes[i].equals(namespace))))
      {
        return this.attributes[(i + 3)];
      }
    }
    return null;
  }

  public int getEventType() throws XmlPullParserException {
    return this.type;
  }

  public int next() throws XmlPullParserException, IOException
  {
    this.txtPos = 0;
    this.isWhitespace = true;
    int minType = 9999;
    this.token = false;
    do
    {
      nextImpl();
      if (this.type < minType) {
        minType = this.type;
      }
    }

    while ((minType > 6) || ((minType >= 4) && (peekType() >= 4)));

    this.type = minType;
    if (this.type > 4) {
      this.type = 4;
    }
    return this.type;
  }

  public int nextToken() throws XmlPullParserException, IOException
  {
    this.isWhitespace = true;
    this.txtPos = 0;

    this.token = true;
    nextImpl();
    return this.type;
  }

  public int nextTag()
    throws XmlPullParserException, IOException
  {
    next();
    if ((this.type == 4) && (this.isWhitespace)) {
      next();
    }
    if ((this.type != 3) && (this.type != 2)) {
      exception("unexpected type");
    }
    return this.type;
  }

  public void require(int type, String namespace, String name)
    throws XmlPullParserException, IOException
  {
    if ((type != this.type) || ((namespace != null) && (!namespace.equals(getNamespace()))) || ((name != null) && (!name.equals(getName()))))
    {
      exception("expected: " + TYPES[type] + " {" + namespace + "}" + name);
    }
  }

  public String nextText() throws XmlPullParserException, IOException {
    if (this.type != 2) {
      exception("precondition: START_TAG");
    }
    next();
    String result;
    if (this.type == 4) {
      result = getText();
      next();
    }
    else {
      result = "";
    }
    if (this.type != 3) {
      exception("END_TAG expected");
    }
    return result;
  }

  public void setFeature(String feature, boolean value) throws XmlPullParserException
  {
    if ("http://xmlpull.org/v1/doc/features.html#process-namespaces".equals(feature))
      this.processNsp = value;
    else if (isProp(feature, false, "relaxed"))
      this.relaxed = value;
    else
      exception("unsupported feature: " + feature);
  }

  public void setProperty(String property, Object value) throws XmlPullParserException
  {
    if (isProp(property, true, "location"))
      this.location = value;
    else
      throw new XmlPullParserException("unsupported property: " + property);
  }

  public void skipSubTree()
    throws XmlPullParserException, IOException
  {
    require(2, null, null);
    int level = 1;
    while (level > 0) {
      int eventType = next();
      if (eventType == 3) {
        level--;
      }
      else if (eventType == 2)
        level++;
    }
  }
}
